//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-06-07     ZhangYihua   first version
//
// Description  : 
//################################################################################
module tiny_afifo #(
parameter           DEPTH                   = 32,       // must DEPTH=2*n (n>=1)
parameter           DATA_BW                 = 18,
parameter           OUT_REG_EN              = 1'b1,     // read data is REG, 1'b1 means that real total number of FIFO entry is DEPTH+1
//parameter           OUT_REG_EN              = 1'b0,   // read data is combination logic
parameter           SYNC_NUM_W2R            = 3,
parameter           SYNC_NUM_R2W            = 3,

// the following parameters are calculated automatically
parameter           ADDR_BW                 = $clog2(DEPTH),    // address range [0:DEPTH-1]
parameter           CNT_BW                  = $clog2(DEPTH+1)   // fifo_cnt   range [0:DEPTH]
) ( 
// the following ports based on clk_wr
input                                       rst_wr_n,
input                                       clk_wr,

input                                       fifo_wen,
input               [DATA_BW-1:0]           fifo_wdata,
output                                      fifo_full,          // full==1 means 'DEPTH+prefetch' data in FIFO and read REG pipeline
output                                      fifo_afull,         // afull==0 means free entry in FIFO is at least 'DEPTH-afull_th'
output              [CNT_BW-1:0]            fifo_wcnt,          // wcnt is not include synchronization pipeline number and prefetch_cnt

input               [ADDR_BW-1:0]           cfg_fifo_afull_th,  // afull_th<fifo_wcnt<=DEPTH is afull, range is [0:DEPTH-1]
output                                      err_fifo_wilf,      // fifo internal logic fault
output                                      err_fifo_wof,       // fifo write overflow

// the following ports based on clk_rd
input                                       rst_rd_n,
input                                       clk_rd,

input                                       fifo_ren,
output              [DATA_BW-1:0]           fifo_rdata,         // zero read latency even when REG_OUT_EN==1'b1, rdata is valid before ren==1'b1
output                                      fifo_empty,         // 1'b1: fifo_rdata is invalid; 1'b0: fifo_rdata is valid at same clock cycle
output                                      fifo_aempty,        // aempty==0 means data number in FIFO (exclude prefetch_cnt) is at least 'aempty_th' 
output              [CNT_BW-1:0]            fifo_rcnt,          // rcnt is not include synchronization pipeline number and prefetch_cnt
output                                      prefetch_cnt,       // number of data cached in out REG, not used when OUT_REG_EN==1'b0

input               [ADDR_BW-1:0]           cfg_fifo_aempty_th, // 0<=fifo_cnt<aempty_th is aempty, range is [1:min(DEPTH,2^ADDR_BW-1)]
output                                      err_fifo_rilf,      // report internal logic fault, and fifo can resotre automatically
output                                      err_fifo_ruf        // fifo read underflow
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                                        raw_fifo_ren;
wire                                        raw_fifo_empty;
wire                                        raw_fifo_ruf;

wire                                        mem_wen;
wire                                        mem_ren;
wire                [ADDR_BW-1:0]           mem_waddr;
wire                [ADDR_BW-1:0]           mem_raddr;
wire                                        mem_rvld_nc;
wire                [DATA_BW-1:0]           mem_rdata;

//################################################################################
// main
//################################################################################

afifo_ctrl #(
        .DEPTH                          (DEPTH                          ),	// must DEPTH=2*n (n>=1)
        .SYNC_NUM_W2R                   (SYNC_NUM_W2R                   ),
        .SYNC_NUM_R2W                   (SYNC_NUM_R2W                   )
) u_afifo_ctrl ( 
        .rst_wr_n                       (rst_wr_n                       ),
        .clk_wr                         (clk_wr                         ),

        .fifo_wen                       (fifo_wen                       ),
        .fifo_full                      (fifo_full                      ),
        .fifo_afull                     (fifo_afull                     ),
        .fifo_wcnt                      (fifo_wcnt                      ),

        .mem_wen                        (mem_wen                        ),
        .mem_waddr                      (mem_waddr                      ),

        .cfg_fifo_afull_th              (cfg_fifo_afull_th              ),
        .err_fifo_wilf                  (err_fifo_wilf                  ),
        .err_fifo_wof                   (err_fifo_wof                   ),

        .rst_rd_n                       (rst_rd_n                       ),
        .clk_rd                         (clk_rd                         ),

        .fifo_ren                       (raw_fifo_ren                   ),
        .fifo_empty                     (raw_fifo_empty                 ),
        .fifo_aempty                    (fifo_aempty                    ),
        .fifo_rcnt                      (fifo_rcnt                      ),

        .mem_ren                        (mem_ren                        ),
        .mem_raddr                      (mem_raddr                      ),

        .cfg_fifo_aempty_th             (cfg_fifo_aempty_th             ),	
        .err_fifo_rilf                  (err_fifo_rilf                  ),
        .err_fifo_ruf                   (raw_fifo_ruf                   )
);

tiny_mem #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .OUT_REG_EN                     (OUT_REG_EN                     ) 	// read data is REG, total depth is DEPTH+1
) u_tiny_mem ( 
        .rst_wr_n                       (rst_wr_n                       ),
        .clk_wr                         (clk_wr                         ),
        .mem_wen                        (mem_wen                        ),
        .mem_waddr                      (mem_waddr                      ),
        .mem_wdata                      (fifo_wdata                     ),

        .rst_rd_n                       (rst_rd_n                       ),
        .clk_rd                         (clk_rd                         ),
        .mem_ren                        (mem_ren                        ),
        .mem_raddr                      (mem_raddr                      ),
        .mem_rvld                       (mem_rvld_nc                    ),
        .mem_rdata                      (mem_rdata                      )
);

generate if (OUT_REG_EN==1'b1) begin:G_REG
    wire                                        mem_rdpl_ce_nc;
    wire                                        raw_fifo_ruf_nc;

    fifo_prefetch #(
            .DATA_BW                        (DATA_BW                        ),
            .RDPL_NUM                       (1                              ),	// read data pipeline number 
            .ARCH_MODE                      ("RDPL_SHIFT"                   )
    ) u_prefetch ( 
            .rst_n                          (rst_rd_n                       ),
            .clk                            (clk_rd                         ),

    // raw fifo with read data latency > 0
            .raw_fifo_empty                 (raw_fifo_empty                 ),
            .raw_fifo_ren                   (raw_fifo_ren                   ),

    // zero read latency fifo after prefetching
            .zrl_fifo_ren                   (fifo_ren                       ),
            .zrl_fifo_empty                 (fifo_empty                     ),
            .zrl_fifo_rdata                 (fifo_rdata                     ),
            .zrl_fifo_ruf                   (err_fifo_ruf                   ),	// read underflow

            .mem_rdpl_ce                    (mem_rdpl_ce_nc                 ),
            .mem_rdata                      (mem_rdata                      ),

            .prefetch_cnt                   (prefetch_cnt                   )
    );

    assign raw_fifo_ruf_nc   = raw_fifo_ruf;

end else begin:G_NREG

    assign raw_fifo_ren = fifo_ren;

    assign fifo_empty   = raw_fifo_empty;
    assign fifo_rdata   = mem_rdata;
    assign err_fifo_ruf = raw_fifo_ruf;

    assign prefetch_cnt = 1'b0;

end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule

